package br.edu.utfpr.backuptools.BackupTools.util.backup;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import br.edu.utfpr.backuptools.BackupTools.model.pojo.Backup;
import br.edu.utfpr.backuptools.BackupTools.util.backup.interfaces.BackupProvider;

/**
 * Classe utilitaria para compactaçãoo de descompactaçãoo de arquivos
 * 
 * @author Luan Malaguti Reffatti
 * 
 */
public class ZipUtil implements BackupProvider {
	
	public static final String PREFIXO = "_backup";
	public static final String EXT = ".zip";
	
	public ZipUtil() {

	}

	/**
	 * Metodo responsavel por compactar um arquivo ou diretorio
	 * 
	 * @param input origem (arquivo ou diretorio)
	 * @param output destino (arquivo zip de saida)
	 */
	@Override
	public void compress(File input, File output) throws IOException {
		if (!input.exists()) {
			throw new IOException(input.getName() + " não existe!");
		}

		if (output.exists()) {
			if (output.isDirectory()) {
				throw new IllegalArgumentException("\""
						+ output.getAbsolutePath() + "\" não é um arquivo!");
			}
		} else {
			final File parent = output.getParentFile();
			if (parent != null) {
				parent.mkdirs();
			}
			output.createNewFile();
		}
		final ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(
				output));
		zip.setLevel(Deflater.BEST_COMPRESSION);
		compress("", input, zip);
		zip.finish();
		zip.flush();
		zip.close();
	}
	
	/**
	 * Extrai um arquivo zip para um diretorio de destino
	 * 
	 * @param arquivoZip arquivo compactado no formato zip
	 * @param diretorio local de destino da extração
	 */
	@Override
	public void extrairZip(File arquivoZip, File diretorio) throws ZipException, IOException {
		ZipFile zip = null;
		File arquivo = null;
		InputStream is = null;
		OutputStream os = null;
		byte[] buffer = new byte[4096];
		try {
			// cria diretório informado, caso não exista
			if (!diretorio.exists()) {
				diretorio.mkdirs();
			}
			if (!diretorio.exists() || !diretorio.isDirectory()) {
				throw new IOException("Informe um diretório válido");
			}
			zip = new ZipFile(arquivoZip);
			Enumeration e = zip.entries();
			while (e.hasMoreElements()) {
				ZipEntry entrada = (ZipEntry) e.nextElement();
				arquivo = new File(diretorio, entrada.getName());
				// se for diretório inexistente, cria a estrutura
				// e pula pra próxima entrada
				if (entrada.isDirectory() && !arquivo.exists()) {
					arquivo.mkdirs();
					continue;
				}
				// se a estrutura de diretórios não existe, cria
				if (!arquivo.getParentFile().exists()) {
					arquivo.getParentFile().mkdirs();
				}
				try {
					// lê o arquivo do zip e grava em disco
					is = zip.getInputStream(entrada);
					os = new FileOutputStream(arquivo);
					int bytesLidos = 0;
					if (is == null) {
						throw new ZipException("Erro ao ler a entrada do zip: "
								+ entrada.getName());
					}
					while ((bytesLidos = is.read(buffer)) > 0) {
						os.write(buffer, 0, bytesLidos);
					}
				} finally {
					if (is != null) {
						try {
							is.close();
						} catch (Exception ex) {
						}
					}
					if (os != null) {
						try {
							os.close();
						} catch (Exception ex) {
						}
					}
				}
			}
		} finally {
			if (zip != null) {
				try {
					zip.close();
				} catch (Exception e) {
				}
			}
		}
	}

	// Adiciona determinado arquivo ao ZIP
	private void compress(String caminho, File arquivo, ZipOutputStream saida)
			throws IOException {
		final boolean dir = arquivo.isDirectory();
		final String nome = arquivo.getName();
		final ZipEntry elemento = new ZipEntry(caminho + '/' + nome
				+ (dir ? "/" : ""));
		elemento.setSize(arquivo.length());
		elemento.setTime(arquivo.lastModified());
		saida.putNextEntry(elemento);
		if (dir) {
			final File[] arquivos = arquivo.listFiles();
			for (int i = 0; i < arquivos.length; i++) {
				// recursivamente adiciona outro arquivo ao ZIP
				compress(caminho + '/' + nome, arquivos[i], saida);
			}
		} else {
			final FileInputStream entrada = new FileInputStream(arquivo);
			copy(entrada, saida);
			entrada.close();
		}
	}

	// Copia o conteúdo do stream de entrada para o stream de saída
	private static void copy(final InputStream in, final OutputStream out)
			throws IOException {
		final int n = 4096;
		final byte[] b = new byte[n];
		for (int r = -1; (r = in.read(b, 0, n)) != -1; out.write(b, 0, r)) {
		}
		out.flush();
	}
	
	public static boolean diretorioExiste(Backup backup){
		return new File(backup.getOrigem()).exists();
	}
	
	public static void deleteDiretorio(Backup backup){
		deleteDir(new File(backup.getOrigem()));
	}
	
	/**
	 * Deleta uma pasta do sistema
	 * 
	 * @param dir diretorio a ser excluido
	 * @return
	 */
	protected static boolean deleteDir(File dir) {  
        if (dir.isDirectory()) {  
            String[] children = dir.list();  
            for (int i=0; i<children.length; i++) {   
               boolean success = deleteDir(new File(dir, children[i]));  
                if (!success) {  
                    return false;  
                }  
            }  
        }  
  
        return dir.delete();  
    }  

}
